namespace nesemu
{
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Windows.Input;

    public delegate void StateSavedDelegate(byte[] saveData);

    public delegate void NotifyDelegate();

    public class GameFieldEvents
    {
        public event StateSavedDelegate StateSaved = null;
        public event NotifyDelegate StateLoaded = null;

        internal void InvokeStateLoaded()
        {
            NotifyDelegate evt = StateLoaded;
            if (evt != null)
            {
                evt();
            }
        }

        internal event NotifyDelegate GamePaused = null;

        internal void InvokeGamePaused()
        {
            NotifyDelegate evt = GamePaused;
            if (evt != null)
            {
                evt();
            }
        }

        internal event NotifyDelegate GameStarted = null;

        internal void InvokeGameStarted()
        {
            NotifyDelegate evt = GameStarted;
            if (evt != null)
            {
                evt();
            }
        }

        internal void InvokeStateSaved(byte[] saveData)
        {
            StateSavedDelegate evt = StateSaved;
            if (evt != null)
            {
                evt(saveData);
            }
        }
    }

    [DataContract]
    public class GameFieldConfig
    {
        [DataMember]
        public GameKeysConfig Player1 { get; set; }
        
        [DataMember]
        public GameKeysConfig Player2 { get; set; }
        
        [DataMember]
        public bool SoundEnabled { get; set; }

        public GameFieldEvents Events { get; set; }

        [DataMember]
        public Key QuickLoad { get; set; }
        [DataMember]
        public Key QuickSave { get; set; }

        public byte[] LoadData { get; set; }

        public GameFieldConfig()
        {
            Player1 = GameKeysConfig.GetPredefinedForPlayer1();
            Player2 = GameKeysConfig.GetPredefinedForPlayer2();
            Events = new GameFieldEvents();
            SoundEnabled = true;
            LoadData = null;
            QuickLoad = Key.F9;
            QuickSave = Key.F8;
        }

        public byte[] SaveState()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(GameFieldConfig));
                serializer.WriteObject(stream, this);
                //Set capacity
                return GetTrueMemoryBuffer(stream);
            }

        }

        internal static byte[] GetTrueMemoryBuffer(MemoryStream stream)
        {
            byte[] filledBuffer = new byte[stream.Length];
            Array.Copy(stream.GetBuffer(), filledBuffer, (int)stream.Length);
            return filledBuffer;
        }

        public static GameFieldConfig LoadState(byte[] buffer)
        {
            using (MemoryStream stream = new MemoryStream(buffer, 0, buffer.Length, false))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(GameFieldConfig));
                return (GameFieldConfig)serializer.ReadObject(stream);
            }
        }
    }

    [DataContract]
    public class GameKeysConfig
    {
        [DataMember]
        public int A { get; set; }
        [DataMember]
        public int B { get; set; }
        [DataMember]
        public int Select { get; set; }
        [DataMember]
        public int Start { get; set; }
        [DataMember]
        public int Up { get; set; }
        [DataMember]
        public int Down { get; set; }
        [DataMember]
        public int Left { get; set; }
        [DataMember]
        public int Right { get; set; }

        internal static GameKeysConfig GetPredefinedForPlayer1()
        {
            GameKeysConfig cfg = new GameKeysConfig();
            cfg.A = (int) Key.Z;
            cfg.B = (int) Key.X;
            cfg.Select = (int)Key.Space;
            cfg.Start = (int)Key.Enter;
            cfg.Left = (int)Key.Left;
            cfg.Right = (int)Key.Right;
            cfg.Up = (int)Key.Up;
            cfg.Down = (int)Key.Down;
            return cfg;
        }

        internal static GameKeysConfig GetPredefinedForPlayer2()
        {
            GameKeysConfig cfg = new GameKeysConfig();
            cfg.A = (int)Key.NumPad7;
            cfg.B = (int)Key.NumPad9;
            cfg.Left = (int)Key.NumPad4;
            cfg.Right = (int)Key.NumPad6;
            cfg.Up = (int)Key.NumPad8;
            cfg.Down = (int)Key.NumPad2;
            cfg.Select = (int)Key.NumPad0;
            cfg.Start = (int)Key.Delete;
            return cfg;
        }
    }
}